{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "10a585bf",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## BigDL-Nano Inference Example\n",
    "--- \n",
    "This example shows the usage of bigdl-nano pytorch inference pipeline. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "14f6dfab",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/envs/testNotebook/lib/python3.7/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from time import time\n",
    "\n",
    "import torch\n",
    "from torch import nn\n",
    "import torch.nn.functional as F\n",
    "import torchvision\n",
    "from pl_bolts.datamodules import CIFAR10DataModule\n",
    "from pl_bolts.transforms.dataset_normalizations import cifar10_normalization\n",
    "from pytorch_lightning import LightningModule, seed_everything\n",
    "from torch.optim.lr_scheduler import OneCycleLR\n",
    "from torchmetrics.functional import accuracy\n",
    "from bigdl.nano.pytorch.trainer import Trainer\n",
    "from bigdl.nano.pytorch import InferenceOptimizer\n",
    "from bigdl.nano.pytorch.vision import transforms"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57014979",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "### CIFAR10 Data Module\n",
    "---\n",
    "Import the existing data module from bolts and modify the train and test transforms.\n",
    "You could access [CIFAR10](https://www.cs.toronto.edu/~kriz/cifar.html) for a view of the whole dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "35edbaa7",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def prepare_data(data_path, batch_size, num_workers):\n",
    "    train_transforms = transforms.Compose(\n",
    "        [\n",
    "            transforms.RandomCrop(32, 4),\n",
    "            transforms.RandomHorizontalFlip(),\n",
    "            transforms.ToTensor(),\n",
    "            cifar10_normalization()\n",
    "        ]\n",
    "    )\n",
    "\n",
    "    test_transforms = transforms.Compose(\n",
    "        [\n",
    "            transforms.ToTensor(),\n",
    "            cifar10_normalization()\n",
    "        ]\n",
    "    )\n",
    "    cifar10_dm = CIFAR10DataModule(\n",
    "        data_dir=data_path,\n",
    "        batch_size=batch_size,\n",
    "        num_workers=num_workers,\n",
    "        train_transforms=train_transforms,\n",
    "        test_transforms=test_transforms,\n",
    "        val_transforms=test_transforms\n",
    "    )\n",
    "    return cifar10_dm"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "780de39c",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "### Resnet\n",
    "___\n",
    "Modify the pre-existing Resnet architecture from TorchVision. The pre-existing architecture is based on ImageNet images (224x224) as input. So we need to modify it for CIFAR10 images (32x32)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "bf4d2f1e",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def create_model():\n",
    "    model = torchvision.models.resnet18(pretrained=False, num_classes=10)\n",
    "    model.conv1 = nn.Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
    "    model.maxpool = nn.Identity()\n",
    "    return model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fe7f93f",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "### Lightning Module\n",
    "___\n",
    "Check out the [configure_optimizers](https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#configure-optimizers) method to use custom Learning Rate schedulers. The OneCycleLR with SGD will get you to around 92-93% accuracy in 20-30 epochs and 93-94% accuracy in 40-50 epochs. Feel free to experiment with different LR schedules from https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7f728795",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "class LitResnet(LightningModule):\n",
    "\n",
    "    def __init__(self, learning_rate=0.05):\n",
    "        super().__init__()\n",
    "\n",
    "        self.save_hyperparameters()\n",
    "        self.model = create_model()\n",
    "        self.example_input_array = torch.Tensor(64, 3, 32, 32)\n",
    "\n",
    "    def forward(self, x):\n",
    "        out = self.model(x)\n",
    "        return F.log_softmax(out, dim=1)\n",
    "\n",
    "    def training_step(self, batch, batch_idx):\n",
    "        x, y = batch\n",
    "        logits = self(x)\n",
    "        loss = F.nll_loss(logits, y)\n",
    "        self.log(\"train_loss\", loss)\n",
    "        return loss\n",
    "\n",
    "    def evaluate(self, batch, stage=None):\n",
    "        x, y = batch\n",
    "        logits = self(x)\n",
    "        loss = F.nll_loss(logits, y)\n",
    "        preds = torch.argmax(logits, dim=1)\n",
    "        acc = accuracy(preds, y, 'multiclass', num_classes=10)\n",
    "\n",
    "        if stage:\n",
    "            self.log(f\"{stage}_loss\", loss, prog_bar=True)\n",
    "            self.log(f\"{stage}_acc\", acc, prog_bar=True)\n",
    "\n",
    "    def validation_step(self, batch, batch_idx):\n",
    "        x, y = batch\n",
    "        logits = self(x)\n",
    "        loss = F.nll_loss(logits, y)\n",
    "        preds = torch.argmax(logits, dim=1)\n",
    "        acc = accuracy(preds, y, 'multiclass', num_classes=10)\n",
    "        self.log(\"val_loss\", loss, prog_bar=True)\n",
    "        self.log(\"val_acc\", acc, prog_bar=True)\n",
    "\n",
    "    def test_step(self, batch, batch_idx):\n",
    "        x, y = batch\n",
    "        logits = self(x)\n",
    "        loss = F.nll_loss(logits, y)\n",
    "        preds = torch.argmax(logits, dim=1)\n",
    "        acc = accuracy(preds, y, 'multiclass', num_classes=10)\n",
    "        self.log_dict({'test_loss': loss, 'test_acc': acc})\n",
    "\n",
    "    def configure_optimizers(self):\n",
    "        optimizer = torch.optim.SGD(\n",
    "            self.parameters(),\n",
    "            lr=self.hparams.learning_rate,\n",
    "            momentum=0.9,\n",
    "            weight_decay=5e-4,\n",
    "        )\n",
    "        steps_per_epoch = 45000\n",
    "        scheduler_dict = {\n",
    "            \"scheduler\": OneCycleLR(\n",
    "                optimizer,\n",
    "                0.1,\n",
    "                epochs=self.trainer.max_epochs,\n",
    "                steps_per_epoch=steps_per_epoch,\n",
    "            ),\n",
    "            \"interval\": \"step\",\n",
    "        }\n",
    "        return {\"optimizer\": optimizer, \"lr_scheduler\": scheduler_dict}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7e9da53d",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Global seed set to 7\n"
     ]
    }
   ],
   "source": [
    "seed_everything(7)\n",
    "PATH_DATASETS = os.environ.get(\"PATH_DATASETS\", \".\")\n",
    "BATCH_SIZE = 64\n",
    "NUM_WORKERS = 0\n",
    "data_module = prepare_data(PATH_DATASETS, BATCH_SIZE, NUM_WORKERS)\n",
    "SUBSET = int(os.environ.get('SUBSET', 1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b7d4b74a",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "GPU available: False, used: False\n",
      "TPU available: False, using: 0 TPU cores\n",
      "IPU available: False, using: 0 IPUs\n"
     ]
    }
   ],
   "source": [
    "trainer = Trainer(progress_bar_refresh_rate=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc000cb3",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "### Load Model\n",
    "---\n",
    "Load the LitResnet Model using the checkpoint saving using LightningModule after single process training in the nano-trainer-example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "985d681d",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "pl_model = LitResnet.load_from_checkpoint('checkpoints/model.ckpt')\n",
    "data_module.setup(\"test\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "9dd834b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "mask = list(range(0, len(data_module.test_dataloader().dataset), SUBSET))\n",
    "test_set = torch.utils.data.Subset(data_module.test_dataloader().dataset, mask)\n",
    "from torch.utils.data import DataLoader\n",
    "test_dataloader = DataLoader(test_set, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "3c30c24e",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "start = time()\n",
    "pl_model.eval()\n",
    "for x, _ in test_dataloader:\n",
    "    with torch.no_grad():\n",
    "        pl_model(x)\n",
    "infer_time = time() - start"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01c93f5f",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "### Get Accelerated Module\n",
    "---\n",
    "Use InferenceOptimizer.trace from bigdl.nano.pytorch.InferenceOptimizer to convert a model into an accelerated module for inference.\n",
    "The definition of trace is:\n",
    "```\n",
    "trace(model: nn.Module, input_sample=None, accelerator=None)\n",
    "\n",
    "      :param model: An torch.nn.Module model, including pl.LightningModule.\n",
    "      \n",
    "      :param input_sample: A set of inputs for trace, defaults to None if you have trace before or\n",
    "                             model is a LightningModule with an example_input_array.\n",
    "                             \n",
    "      :param accelerator: The accelerator to use, defaults to None meaning staying in Pytorch\n",
    "                            backend. 'openvino' and 'onnxruntime' are supported for now.\n",
    "                            \n",
    "      :return: Model with different acceleration(OpenVINO/ONNX Runtime).\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "c5d6d517",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "onnx_model = InferenceOptimizer.trace(pl_model, accelerator=\"onnxruntime\", input_sample=torch.Tensor(64, 3, 32, 32))\n",
    "start = time()\n",
    "for x, _ in test_dataloader:\n",
    "    inference_res_onnx = onnx_model(x)\n",
    "onnx_infer_time = time() - start"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "34eb6a8b",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model Optimizer arguments:\n",
      "Common parameters:\n",
      "\t- Path to the Input Model: \t/home/projects/notebooks/tmp.onnx\n",
      "\t- Path for generated IR: \t/home/projects/notebooks/.\n",
      "\t- IR output name: \ttmp\n",
      "\t- Log level: \tERROR\n",
      "\t- Batch: \tNot specified, inherited from the model\n",
      "\t- Input layers: \tNot specified, inherited from the model\n",
      "\t- Output layers: \tNot specified, inherited from the model\n",
      "\t- Input shapes: \tNot specified, inherited from the model\n",
      "\t- Source layout: \tNot specified\n",
      "\t- Target layout: \tNot specified\n",
      "\t- Layout: \tNot specified\n",
      "\t- Mean values: \tNot specified\n",
      "\t- Scale values: \tNot specified\n",
      "\t- Scale factor: \tNot specified\n",
      "\t- Precision of IR: \tFP32\n",
      "\t- Enable fusing: \tTrue\n",
      "\t- User transformations: \tNot specified\n",
      "\t- Reverse input channels: \tFalse\n",
      "\t- Enable IR generation for fixed input shape: \tFalse\n",
      "\t- Use the transformations config file: \tNone\n",
      "Advanced parameters:\n",
      "\t- Force the usage of legacy Frontend of Model Optimizer for model conversion into IR: \tFalse\n",
      "\t- Force the usage of new Frontend of Model Optimizer for model conversion into IR: \tFalse\n",
      "OpenVINO runtime found in: \t/opt/conda/envs/inferenceWithOpenvino/lib/python3.7/site-packages/openvino\n",
      "OpenVINO runtime version: \t2022.1.0-7019-cdb9bec7210-releases/2022/1\n",
      "Model Optimizer version: \t2022.1.0-7019-cdb9bec7210-releases/2022/1\n",
      "[ SUCCESS ] Generated IR version 11 model.\n",
      "[ SUCCESS ] XML file: /home/projects/notebooks/tmp.xml\n",
      "[ SUCCESS ] BIN file: /home/projects/notebooks/tmp.bin\n",
      "[ SUCCESS ] Total execution time: 0.72 seconds. \n",
      "[ SUCCESS ] Memory consumed: 221 MB. \n",
      "It's been a while, check for a new version of Intel(R) Distribution of OpenVINO(TM) toolkit here https://software.intel.com/content/www/us/en/develop/tools/openvino-toolkit/download.html?cid=other&source=prod&campid=ww_2022_bu_IOTG_OpenVINO-2022-1&content=upg_all&medium=organic or on the GitHub*\n",
      "[ INFO ] The model was converted to IR v11, the latest model format that corresponds to the source DL framework input/output format. While IR v11 is backwards compatible with OpenVINO Inference Engine API v1.0, please use API v2.0 (as of 2022.1) to take advantage of the latest improvements in IR v11.\n",
      "Find more information about API v2.0 and IR v11 at https://docs.openvino.ai\n"
     ]
    }
   ],
   "source": [
    "openvino_model = InferenceOptimizer.trace(pl_model, accelerator=\"openvino\")\n",
    "start = time()\n",
    "for x, _ in test_dataloader:\n",
    "    openvino_model(x)\n",
    "openvino_infer_time = time() - start"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "92d1ed8d",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "|    Precision   | Inference Time(s) |\n",
      "|     Pytorch    |       15.72       |\n",
      "|      ONNX      |        7.65       |\n",
      "|    Openvino    |        5.99       |\n",
      "\n"
     ]
    }
   ],
   "source": [
    "template = \"\"\"\n",
    "|    Precision   | Inference Time(s) |\n",
    "|     Pytorch    |       {:5.2f}       |\n",
    "|      ONNX      |       {:5.2f}       |\n",
    "|    Openvino    |       {:5.2f}       |\n",
    "\"\"\"\n",
    "summary = template.format(\n",
    "    \n",
    "    infer_time,\n",
    "    onnx_infer_time,\n",
    "    openvino_infer_time\n",
    ")\n",
    "print(summary)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8dccf556",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "### Quantize Model\n",
    "Use InferenceOptimizer.quantize from bigdl.nano.pytorch.InferenceOptimizer to calibrate a model for post-training quantization. Here are some parameters that might be useful to you:\n",
    "```\n",
    ":param model:            A model to be quantized. Model should be an instance of torch.nn.Module\n",
    ":param precision         Global precision of quantized model,\n",
    "                         supported type: 'int8', 'bf16', 'fp16', defaults to 'int8'.\n",
    ":param accelerator:      Use accelerator 'None', 'onnxruntime', 'openvino', defaults to None.\n",
    "                         None means staying in pytorch.\n",
    ":param calib_data:       A torch.utils.data.dataloader.DataLoader object for calibration.     \n",
    "                                 Required for static quantization.\n",
    ":param approach:         'static' or 'dynamic'.\n",
    "                         'static': post_training_static_quant,\n",
    "                         'dynamic': post_training_dynamic_quant.\n",
    "                          Default: 'static'.\n",
    ":input_sample:           An input example to convert pytorch model into ONNX/OpenVINO.\n",
    "                          \n",
    ":return                  An accelerated torch.nn.Module if quantization is sucessful.\n",
    "\n",
    "```\n",
    "Access more details from [Source](https://github.com/intel-analytics/BigDL/blob/main/python/nano/src/bigdl/nano/pytorch/inference/optimizer.py#L452)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "fd3b4292",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-05-26 00:55:06 [INFO] Generate a fake evaluation function.\n",
      "2022-05-26 00:55:06 [INFO] Pass query framework capability elapsed time: 62.73 ms\n",
      "2022-05-26 00:55:06 [INFO] Get FP32 model baseline.\n",
      "2022-05-26 00:55:06 [INFO] Save tuning history to /home/projects/notebooks/nc_workspace/2022-05-26_00-55-06/./history.snapshot.\n",
      "2022-05-26 00:55:06 [INFO] FP32 baseline is: [Accuracy: 1.0000, Duration (seconds): 0.0000]\n",
      "2022-05-26 00:55:07 [WARNING] Please note that calibration sampling size 100 isn't divisible exactly by batch size 64. So the real sampling size is 128.\n",
      "/opt/conda/envs/inferenceWithOpenvino/lib/python3.7/site-packages/torch/_tensor.py:575: UserWarning: floor_divide is deprecated, and will be removed in a future version of pytorch. It currently rounds toward 0 (like the 'trunc' function NOT 'floor'). This results in incorrect rounding for negative values.\n",
      "To keep the current behavior, use torch.div(a, b, rounding_mode='trunc'), or for actual floor division, use torch.div(a, b, rounding_mode='floor'). (Triggered internally at  /pytorch/aten/src/ATen/native/BinaryOps.cpp:467.)\n",
      "  return torch.floor_divide(self, other)\n",
      "2022-05-26 00:55:08 [INFO] |********Mixed Precision Statistics*******|\n",
      "2022-05-26 00:55:08 [INFO] +------------------------+--------+-------+\n",
      "2022-05-26 00:55:08 [INFO] |        Op Type         | Total  |  INT8 |\n",
      "2022-05-26 00:55:08 [INFO] +------------------------+--------+-------+\n",
      "2022-05-26 00:55:08 [INFO] |  quantize_per_tensor   |   1    |   1   |\n",
      "2022-05-26 00:55:08 [INFO] |       ConvReLU2d       |   9    |   9   |\n",
      "2022-05-26 00:55:08 [INFO] |        Identity        |   1    |   1   |\n",
      "2022-05-26 00:55:08 [INFO] |         Conv2d         |   11   |   11  |\n",
      "2022-05-26 00:55:08 [INFO] |        add_relu        |   8    |   8   |\n",
      "2022-05-26 00:55:08 [INFO] |   AdaptiveAvgPool2d    |   1    |   1   |\n",
      "2022-05-26 00:55:08 [INFO] |        flatten         |   1    |   1   |\n",
      "2022-05-26 00:55:08 [INFO] |         Linear         |   1    |   1   |\n",
      "2022-05-26 00:55:08 [INFO] |       dequantize       |   1    |   1   |\n",
      "2022-05-26 00:55:08 [INFO] +------------------------+--------+-------+\n",
      "2022-05-26 00:55:08 [INFO] Pass quantize model elapsed time: 1037.18 ms\n",
      "2022-05-26 00:55:08 [INFO] Tune 1 result is: [Accuracy (int8|fp32): 1.0000|1.0000, Duration (seconds) (int8|fp32): 0.0000|0.0000], Best tune result is: [Accuracy: 1.0000, Duration (seconds): 0.0000]\n",
      "2022-05-26 00:55:08 [INFO] |**********************Tune Result Statistics**********************|\n",
      "2022-05-26 00:55:08 [INFO] +--------------------+----------+---------------+------------------+\n",
      "2022-05-26 00:55:08 [INFO] |     Info Type      | Baseline | Tune 1 result | Best tune result |\n",
      "2022-05-26 00:55:08 [INFO] +--------------------+----------+---------------+------------------+\n",
      "2022-05-26 00:55:08 [INFO] |      Accuracy      | 1.0000   |    1.0000     |     1.0000       |\n",
      "2022-05-26 00:55:08 [INFO] | Duration (seconds) | 0.0000   |    0.0000     |     0.0000       |\n",
      "2022-05-26 00:55:08 [INFO] +--------------------+----------+---------------+------------------+\n",
      "2022-05-26 00:55:08 [INFO] Save tuning history to /home/projects/notebooks/nc_workspace/2022-05-26_00-55-06/./history.snapshot.\n",
      "2022-05-26 00:55:08 [INFO] Specified timeout or max trials is reached! Found a quantized model which meet accuracy goal. Exit.\n",
      "2022-05-26 00:55:08 [INFO] Save deploy yaml to /home/projects/notebooks/nc_workspace/2022-05-26_00-55-06/deploy.yaml\n"
     ]
    }
   ],
   "source": [
    "i8_model = InferenceOptimizer.quantize(pl_model, calib_data=test_dataloader)\n",
    "start = time()\n",
    "for x, _ in test_dataloader:\n",
    "    inference_res_i8 = i8_model(x)\n",
    "i8_inference_time = time() - start"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "390f5adf",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "i8_acc = 0.0\n",
    "fp32_acc = 0.0\n",
    "for x, y in test_dataloader:\n",
    "    output_i8 = i8_model(x)\n",
    "    output_fp32 = pl_model(x)\n",
    "    i8_acc += accuracy(output_i8, y, 'multiclass', num_classes=10)\n",
    "    fp32_acc += accuracy(output_fp32, y, 'multiclass', num_classes=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "be509946",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "i8_acc = i8_acc/len(test_dataloader)\n",
    "fp32_acc = fp32_acc/len(test_dataloader)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "4152526c",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "|    Precision   | Inference Time(s) | Accuracy(%) |\n",
      "|      FP32      |       15.72       |    0.8619    |\n",
      "|      INT8      |        4.70       |    0.8608    |\n",
      "| Improvement(%) |       70.09       |    -0.0011    |\n",
      "\n"
     ]
    }
   ],
   "source": [
    "template = \"\"\"\n",
    "|    Precision   | Inference Time(s) | Accuracy(%) |\n",
    "|      FP32      |       {:5.2f}       |    {:5.4f}    |\n",
    "|      INT8      |       {:5.2f}       |    {:5.4f}    |\n",
    "| Improvement(%) |       {:5.2f}       |    {:5.4f}    |\n",
    "\"\"\"\n",
    "summary = template.format(\n",
    "    infer_time, fp32_acc,\n",
    "    i8_inference_time, i8_acc,\n",
    "    (1 - i8_inference_time /infer_time) * 100,\n",
    "    i8_acc - fp32_acc\n",
    ")\n",
    "print(summary)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ddac844",
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:testNotebook]",
   "language": "python",
   "name": "conda-env-testNotebook-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
